package com.zrq.secondkill.service.impl;

import com.zrq.secondkill.constant.Constant;
import com.zrq.secondkill.dao.ItemStockDOMapper;
import com.zrq.secondkill.dao.PromoDOMapper;
import com.zrq.secondkill.dataobject.ItemStockDO;
import com.zrq.secondkill.dataobject.PromoDO;
import com.zrq.secondkill.enums.PromoStatusEnum;
import com.zrq.secondkill.error.BusinessErrorEnum;
import com.zrq.secondkill.error.BusinessException;
import com.zrq.secondkill.response.CommonReturnType;
import com.zrq.secondkill.service.ItemService;
import com.zrq.secondkill.service.PromoService;
import com.zrq.secondkill.service.UniqueIdService;
import com.zrq.secondkill.service.UserService;
import com.zrq.secondkill.service.model.ItemModel;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service
public class PormoServiceImpl implements PromoService {

    private final PromoDOMapper promoDOMapper;

    private final ItemStockDOMapper itemStockDOMapper;

    private final RedisTemplate redisTemplate;

    private final ItemService itemService;

    private final UserService userService;

    private final UniqueIdService uniqueIdService;

    /**
     * 库存缓存前缀,key格式: STOCK_CACHE_PREFIX + itemId
     */
    private static final String STOCK_CACHE_PREFIX = "stockCache";

    /**
     * 秒杀令牌前缀,key格式: PROMO_TOKEN_PREFIX + userId +"itemId" +itemId
     */
    private static final String PROMO_TOKEN_PREFIX = "pTokenUid";

    /**
     * 令牌已发放数量前缀,key格式: PROMO_TOKEN_NUM_PREFIX+itemId
     */
    private static final String PROMO_TOKEN_NUM_PREFIX="pTokenNum";

    /**
     * 令牌最少发放库存的多少倍
     */
    @Value("${promo.token.multiplier}")
    private Integer tokenMultiplier;

    public PormoServiceImpl(PromoDOMapper promoDOMapper, ItemStockDOMapper itemStockDOMapper, RedisTemplate redisTemplate, ItemService itemService, UserService userService, UniqueIdService uniqueIdService) {
        this.promoDOMapper = promoDOMapper;
        this.itemStockDOMapper = itemStockDOMapper;
        this.redisTemplate = redisTemplate;
        this.itemService = itemService;
        this.userService = userService;
        this.uniqueIdService = uniqueIdService;
    }

    @Override
    public void publishPromo(Integer promoId) throws BusinessException {
        PromoDO promoDO = promoDOMapper.selectByPrimaryKey(promoId);
        if (promoDO == null) {
            throw new BusinessException(BusinessErrorEnum.PARAMETER_VALIDATION_ERROR, "要发布的promoID在数据库中不存在");
        }

        Integer itemId = promoDO.getItemId();
        ItemStockDO itemStockDO = itemStockDOMapper.selectByItemId(itemId);

        String cacheKey = STOCK_CACHE_PREFIX + itemId;
        redisTemplate.opsForValue().set(cacheKey,itemStockDO.getStock(),1,TimeUnit.DAYS);
//        redisTemplate.opsForHash().put(cacheKey, "stock", itemStockDO.getStock());
//        redisTemplate.opsForHash().put(cacheKey, "itemId", itemId);

//        redisTemplate.expire(cacheKey, 1, TimeUnit.DAYS);
    }

    @Override
    public String generatePromoItemToken(Integer userId, Integer itemId, Integer promoId) throws BusinessException {
        //检查令牌用尽标识
        Object flag=redisTemplate.opsForValue().get(Constant.TOKEN_ENOUGH_PREFIX+itemId);
        if(flag!=null){
            throw new BusinessException(BusinessErrorEnum.STOCK_NOT_ENOUGH,"秒杀令牌已用尽,获取秒杀令牌失败");
        }

        //TODO(zrq): 库存缓存不应该用hash结构,itemId是多余的
        //计算令牌是否已经发放够多了
        Integer stock= (Integer) redisTemplate.opsForValue().get(STOCK_CACHE_PREFIX + itemId);
        if(stock==null){
            throw new BusinessException(BusinessErrorEnum.PARAMETER_VALIDATION_ERROR,"缓存中没有商品库存信息,获取秒杀令牌失败");
        }

        //增加令牌发放计数
        Long tokenNum =  redisTemplate.opsForValue().increment(PROMO_TOKEN_NUM_PREFIX + itemId,1);
        if(tokenNum==null){
            tokenNum=0L;
            redisTemplate.opsForValue().set(PROMO_TOKEN_NUM_PREFIX+itemId,1);
        }
        if(tokenNum>stock*tokenMultiplier){
            redisTemplate.opsForValue().set(Constant.TOKEN_ENOUGH_PREFIX+itemId,1);
            throw new BusinessException(BusinessErrorEnum.STOCK_NOT_ENOUGH,"秒杀令牌已用尽,获取秒杀令牌失败");
        }

        //校验商品信息
        ItemModel itemModel = itemService.getItemByIdInCache(itemId);
        if (itemModel == null) {
            throw new BusinessException(BusinessErrorEnum.PARAMETER_VALIDATION_ERROR, "商品不存在");
        }

        //校验用户信息
        if (!userService.validateUserId(userId)) {
            throw new BusinessException(BusinessErrorEnum.PARAMETER_VALIDATION_ERROR, "用户信息校验不通过");
        }

        //校验促销信息
        if (promoId != null) {
            if (!promoId.equals(itemModel.getPromoModel().getId())) {
                throw new BusinessException(BusinessErrorEnum.PARAMETER_VALIDATION_ERROR, "商品与促销活动不匹配");
            }
            if (itemModel.getPromoModel().getStatus() != PromoStatusEnum.ON_GOING) {
                throw new BusinessException(BusinessErrorEnum.PARAMETER_VALIDATION_ERROR, "不在活动时间内");
            }
        }

        String token = uniqueIdService.generateUniqueId();

        String key = PROMO_TOKEN_PREFIX + userId + "itemId" + itemId;

        redisTemplate.opsForValue().set(key, token,30,TimeUnit.MINUTES);

        return token;
    }

    @Override
    public Boolean validatePromoItemToken(String token, Integer userId, Integer itemId) {

        String key = PROMO_TOKEN_PREFIX + userId + "itemId" + itemId;

        String redisToken = (String) redisTemplate.opsForValue().get(key);

        return StringUtils.equals(token,redisToken);
    }
}
